package org.zh.client.config;

import com.alibaba.druid.util.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import org.zh.client.cache.RemoteCache;
import org.zh.client.jdbc.TCCache;
import org.zh.entity.MethodBean;

import javax.sql.DataSource;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.http.HttpResponse;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

import static org.zh.client.cache.RemoteCache.localCache;
import static org.zh.util.CommonStatic.*;


/**
 * 初始化是相对于spring 来说的   before() 在spring初始化之前  进行需要的全局系统变量的初始化System.setProperty（）
 * after()  在spring初始化完之后   主要是一些其他的需要spring资源的  初始化
 */
public class ZHOpenInit {
    public static final String serverPort = "app.server.port";
    public static final String serverContextPath = "app.server.context-path";
    public static final String appName = "app.application.name";
    public static final String appVersion = "app.application.version";
    public static final String appIpAddress = "app.local.ipAddress";
    public static final String httpBasePath = "app.http.basePath";
    public static final String zhOpenClientTaskDelay = "zhOpen.clientTask.delay";
    public static final String zhOpenClientTaskPeriod = "zhOpen.clientTask.period";
    public static final String zhOpenTransactionTaskKeepTime = "zhOpen.transactionTask.keepTime";
    public static final String zhOpenTransactionTaskDelay = "zhOpen.transactionTask.delay";
    public static final String zhOpenTransactionTaskPeriod = "zhOpen.transactionTask.period";
    public static final String zhOpenLogEnv = "zhOpen.logEnv";
    public static final String zhOpenLogName = "zhOpen.logName";
    public static final String zhOpenTables = "zhOpen.tables";
    public static final String zhOpenGlobal_TA = "zhOpen.global-ta";
    public static final String zhOpenRCBaseUrl = "zhOpen.rcBaseUrl";

    public static void before() {
        Properties env = new Properties();
        try {
            env.load(ZHOpenInit.class.getClassLoader().getResourceAsStream("application.properties"));
            System.setProperty(serverPort, env.getProperty("server.port"));
            System.setProperty(serverContextPath, env.getProperty("server.servlet.context-path"));
            System.setProperty(appName, env.getProperty("spring.application.name"));
            System.setProperty(appVersion, env.getProperty("spring.application.version"));
            System.setProperty(appIpAddress, InetAddress.getLocalHost().getHostAddress());
            System.setProperty(httpBasePath, "http://" + System.getProperty(appIpAddress) + ":" + env.getProperty("server.port", "8080") + env.getProperty("server.servlet.context-path", ""));
            System.setProperty(zhOpenClientTaskDelay, env.getProperty("zhOpen.clientTask.delay", "10000"));
            System.setProperty(zhOpenClientTaskPeriod, env.getProperty("zhOpen.clientTask.period", "50000"));
            System.setProperty(zhOpenTransactionTaskKeepTime, env.getProperty("zhOpen.transactionTask.keepTime", "60000"));
            System.setProperty(zhOpenTransactionTaskDelay, env.getProperty("zhOpen.transactionTask.delay", "60000"));
            System.setProperty(zhOpenTransactionTaskPeriod, env.getProperty("zhOpen.transactionTask.period", "60000"));
            System.setProperty(zhOpenLogEnv, env.getProperty("zhOpen.logEnv", "dev"));
            System.setProperty(zhOpenLogName, env.getProperty("zhOpen.logName", "app.%u.%g.log"));
            System.setProperty(zhOpenTables, env.getProperty("zhOpen.tables"));
            System.setProperty(zhOpenGlobal_TA, env.getProperty("zhOpen.global-ta", ""));
            System.setProperty(zhOpenRCBaseUrl, env.getProperty("zhOpen.rcBaseUrl", "http://" + System.getProperty(appIpAddress) + ":8848"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void after(Class<?> application) throws JsonProcessingException, ExecutionException, InterruptedException {
        initTableCache();
        initRemoteCache();
    }

    private static void initRemoteCache() throws JsonProcessingException, ExecutionException, InterruptedException {
        CompletableFuture<HttpResponse<String>> future = smartHttpPOSTJSON(System.getProperty(zhOpenRCBaseUrl) + rcRegistry, localCache);
        HttpResponse<String> response = future.get();
        if (response.statusCode() != 200) {
            throw new RuntimeException("请求失败");
        }
        String body = response.body();
        if (body.isEmpty()) return;
        Map<String, List<Map<String, MethodBean>>> map = om.readValue(body, new TypeReference<>() {});
        RemoteCache.putRemote(map);
        RemoteCache.print();
    }

    private static void initTableCache() {
        String property = System.getProperty(zhOpenTables);
        if (StringUtils.isEmpty(property)) {
            throw new RuntimeException("application.properties 没有配置 zhOpen.tables ");
        }
        DataSource dataSource = SpringContextUtil.getBean(DataSource.class);
        String[] tables = property.split(";");
        try {
            for (String table : tables) {
                String sql = "select COLUMN_NAME,COLUMN_KEY,EXTRA FROM INFORMATION_SCHEMA.COLUMNS WHERE  TABLE_NAME = '" + table + "'";
                Connection conn = dataSource.getConnection();
                Statement state = conn.createStatement();
                ResultSet rs = state.executeQuery(sql);
                List<String> list = new ArrayList<>(0);
                while (rs.next()) {
                    if (rs.getString("COLUMN_KEY").equals("PRI") || rs.getString("EXTRA").equals("auto_increment")) {
                        list.add(rs.getString("COLUMN_NAME"));
                    }
                }
                if (list.isEmpty()) {
                    TCCache.put(table, null);
                }
                TCCache.put(table, list);
                rs.close();
                state.close();
                conn.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public static void initTimer() {
        timer.schedule(new TimerTask() {
                           @Override
                           public void run() {
                               try {
                                   HttpResponse<String> response = smartHttpPOST(URI.create(zhOpenRCBaseUrl + rcHeartBeat), getJSONRequestBody(localCache), getJSONResponseBody());
                                   if (response.statusCode() != 200) {
                                       throw new RuntimeException("请求错误::" + response.statusCode());
                                   }
                                   String body = response.body();
                                   if (body.isEmpty()) return;
                                   Map<String, List<Map<String, MethodBean>>> map = om.readValue(body, new TypeReference<>() {
                                   });
                                   RemoteCache.putRemote(map);
                               } catch (JsonProcessingException e) {
                                   throw new RuntimeException(e);
                               }
                           }
                       },
                Long.parseLong(System.getProperty(zhOpenClientTaskDelay)),
                Long.parseLong(System.getProperty(zhOpenClientTaskPeriod)));
    }

}
